package com.guardchina.framework.web.filter;

import com.google.gson.Gson;
import com.guardchina.framework.auth.jwt.JsonWebToken;
import com.guardchina.framework.util.constant.Constants;
import com.guardchina.framework.util.constant.HeaderKeyConst;
import com.guardchina.framework.util.constant.ResultStatus;
import com.guardchina.framework.util.exception.login.TokenExpException;
import com.guardchina.framework.util.exception.login.TokenInvalidException;
import com.guardchina.framework.util.exception.login.TokenRefreshException;
import com.guardchina.framework.util.model.ResultHolder;
import com.guardchina.framework.web.api.TokenValidator;
import com.guardchina.framework.web.model.RequestWrapper;
import com.guardchina.framework.web.model.TimeFeature;
import com.guardchina.framework.web.model.TokenValidatorHolder;
import com.guardchina.framework.web.model.UserFeature;
import com.guardchina.framework.web.properties.LoginProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 描述
 *
 * @author lwb
 * @since 2020/3/19
 */
@Slf4j
public class LoginFilter implements Filter {

    private final JsonWebToken jwt;
    private final LoginProperties properties;
    private final TokenValidatorHolder validatorHolder;

    private static final List<String> EXCLUDE_SWAGGER_PATTERNS = new ArrayList<>();

    static {
        EXCLUDE_SWAGGER_PATTERNS.add("/v2/api-docs");
        EXCLUDE_SWAGGER_PATTERNS.add("/**/*swagger*/**");
    }

    public LoginFilter(JsonWebToken jwt, TokenValidatorHolder tokenValidatorHolder, LoginProperties loginProperties){
        this.jwt = jwt;
        this.validatorHolder = tokenValidatorHolder;
        this.properties = loginProperties;
    }

    @Override
    public void init(FilterConfig filterConfig) {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String uri = request.getRequestURI();
        if(isIgnoreUrl(uri, properties)){
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        String token = request.getHeader(HeaderKeyConst.TOKEN_KEY);
        if(StringUtils.isBlank(token)){
            ResultHolder resultHolder = ResultHolder.error(ResultStatus.TOKEN_NOT_EXIST_EXCEPTION, "您尚未登录", uri);
            response.addHeader(HeaderKeyConst.TOKEN_GET_KEY, "true");
            errorResponse(response, resultHolder);
            return;
        }
        UserFeature userFeature = null;
        try {
            userFeature = jwt.verify(token, UserFeature.class);
            //token校验
            if(validatorHolder != null && !CollectionUtils.isEmpty(validatorHolder.getHolders())){
                for(TokenValidator validator : validatorHolder.getHolders()){
                    validator.verify(userFeature);
                }
            }
        }catch (TokenExpException e){
            log.debug("token:[{}]，已过期", token);
            response.addHeader(HeaderKeyConst.TOKEN_GET_KEY, "true");
            ResultHolder resultHolder = ResultHolder.error(ResultStatus.TOKEN_EXP_EXCEPTION, "登录过期,请重新登录", uri);
            errorResponse(response, resultHolder);
            return;
        }catch (TokenInvalidException e) {
            log.error("token解析异常", e);
            response.addHeader(HeaderKeyConst.TOKEN_GET_KEY, "true");
            ResultHolder resultHolder = ResultHolder.error(ResultStatus.TOKEN_INVALID, e.getShortMessage(), uri);
            errorResponse(response, resultHolder);
            return;
        }catch (TokenRefreshException e) {
            //自动刷新token
            response.addHeader(HeaderKeyConst.TOKEN_REFRESH_KEY, refresh(userFeature));
        }

        String userId = userFeature.getUserKey();

        RequestWrapper requestWrapper = new RequestWrapper(request);
        requestWrapper.addParam(Constants.USER_ID, userId);
        requestWrapper.addHeader(HeaderKeyConst.USER_ID_KEY, userId);

        filterChain.doFilter(requestWrapper, response);
    }

    private void errorResponse(HttpServletResponse response, ResultHolder resultHolder) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(new Gson().toJson(resultHolder));
        response.flushBuffer();
        return;
    }

    @Override
    public void destroy() {

    }

    private boolean isIgnoreUrl(String uri, LoginProperties properties){

        AntPathMatcher pathMatcher = new AntPathMatcher();

        //过滤swagger
        if(properties.getExcludeSwagger() !=  null && properties.getExcludeSwagger()){
            for(String pattern : EXCLUDE_SWAGGER_PATTERNS){
                if(pathMatcher.match(pattern, uri)){
                    return true;
                }
            }
        }

        //指定url过滤
        if(!CollectionUtils.isEmpty(properties.getExcludePatterns())){
            for(String pattern : properties.getExcludePatterns()){
                if(pathMatcher.match(pattern, uri)){
                    return true;
                }
            }
        }
        return false;
    }

    private String refresh(TimeFeature feature){
        feature.setCreateTime(System.currentTimeMillis());
        return jwt.createToken(feature);
    }
}
